package nl.zoidberg.calculon.analyzer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nl.zoidberg.calculon.model.Board;
import nl.zoidberg.calculon.model.Game;
import nl.zoidberg.calculon.model.Piece;

public class GameScorer {
	private static GameScorer instance = getUnweightedScorer();
	
	private static GameScorer getUnweightedScorer() {
		GameScorer rv = new GameScorer();
		rv.addScorer(new MaterialScorer(), 1.0f);
		rv.addScorer(new BishopPairScorer(), 1.0f);
		rv.addScorer(new BishopMobilityScorer(), 1.0f);
		rv.addScorer(new PawnStructureScorer(), 1.0f);
		rv.addScorer(new KnightScorer(), 1.0f);
		rv.addScorer(new RookScorer(), 1.0f);
		rv.addScorer(new KingSafetyScorer(), 1.0f);
		return rv;
	}
	
	private Map<PositionScorer, Float> scorers = new HashMap<PositionScorer, Float>();
	
	public static GameScorer getDefaultScorer() {
		return instance;
	}
	
	public void addScorer(PositionScorer scorer, float weighting) {
		scorers.put(scorer, weighting);
	}
	
	/**
	 * Generate a score - positive is good for the current player. Position scorers however can stick with the 
	 * convention of having white as positive.
	 * 
	 * @param board
	 * @return
	 */
	public float score(Board board) {

		String result = board.getResult();
		
		if(result == Game.RES_DRAW) {
			return 0;
		}
		
		if(result == Game.RES_BLACK_WIN || result == Game.RES_WHITE_WIN) {
			return -100;
		}
		
		float score = 0;
		Map<Byte, List<int[]>> pieceMap = generatePieceMap(board);
		
		for(PositionScorer scorer: scorers.keySet()) {
			score += (scorer.scorePosition(board, pieceMap) * scorers.get(scorer));
		}

		return score * (board.getPlayer() == Piece.WHITE ? 1 : -1);
	}
	
	public static Map<Byte, List<int[]>> generatePieceMap(Board board) {
		Map<Byte, List<int[]>> pieceMap = new HashMap<Byte, List<int[]>>();
		for(int file = 0; file < 8; file++) {
			for(int rank = 0; rank < 8; rank++) {
				byte rPiece = board.getPiece(file, rank);
				if(rPiece == 0) {
					continue;
				}
				List<int[]> locations = pieceMap.get(rPiece);
				if(locations == null) {
					locations = new ArrayList<int[]>();
					pieceMap.put(rPiece, locations);
				}
				locations.add(new int[] { file, rank });
			}
		}
		return pieceMap;
	}
}
